/**
 *  hw_uart.c - Hardware interfaces for uart driver.
 *
 *  Copyright (C) 2008-2009 ZhangHu
 *  All rights reserved.
 *  E-MAIL: anmnmnly@gmail.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "include/irq.h"
#include "drivers/uart.h"
#include "regdef.h"
#include "init_target.h"

#include "hw_uart.h"
extern uart_t *puart0;

/* uart0_rev_isr - uart0 ISR */
static void uart0_rev_isr(void) {
    uword_t tmp;

    bufSend(puart0->prevbuf, (char_t)RdURXH0());
    tmp = rUERSTAT0; //clear error flag;
}

void init_uart_args(struct uart_mode Mode) {
    word_t tmp = 0;

    if((Mode.Baud == 0) || (Mode.Baud > 115200)) {
		return;
    }
    if((Mode.DataLen < 5) || (Mode.DataLen > 8)) {
		return;
    }
    if((Mode.StopLen == 0) || (Mode.StopLen > 2)) {
		return;
    }
    if(Mode.Parity > 3) {
		return;
    }

    tmp = Mode.DataLen - 5;
    tmp |= (Mode.StopLen - 1) << 2;

    if(Mode.Parity == UART_PARITY_ODD) {
		tmp |= 0x4 << 3;
    }else if(Mode.Parity == UART_PARITY_ENV) {
		tmp |= 0x5 << 3;
    }else if(Mode.Parity == UART_PARITY_NONE) {
    	tmp |= 0x0 << 3;
    }

    rUFCON0 = 0x0;     /* FIFO disable */
    rUMCON0 = 0x0;
    rULCON0 = tmp;     /* Normal; Parity; StopLen stop; StopLen bit */
    rUCON0 = 0x205;    /* rx=edge,tx=level, disable timeout interrupt */
                       /* disable rx error interrupt, interrupt or polling */
    rUBRDIV0 = ((int)(MCLK / 16 / Mode.Baud + 0.5) -1); /* set Baud */
    isrInstall(UART0_REV_ISR_NUM, 0, uart0_rev_isr);
    rINTMSK &= 0x03FFFF7F; /* enable uart0 RX interrupt */
}

word_t uart_send_byte(char_t ch) {
    if(ch == '\n') {
        while(!(rUTRSTAT0 & 0x2));
        WrUTXH0('\r');
    }
    while(!(rUTRSTAT0 & 0x2)); /* Wait until THR is empty */
    WrUTXH0(ch);

	return TRUE;
}

uword_t uart_rev_str(char_t *buf, word_t len){
    char_t *str = buf;
    bool_t err = FALSE;

    while(len--) {
        if(bufGetUsed(puart0->prevbuf)) {
            *str = (char_t)bufRev(puart0->prevbuf, &err);
            if(err == TRUE) {
                str++;
            }
        }
    }

    return (str - buf);
}

